<?php
/**
* server part of the CPAINT chat 
*
* http://sf.net/projects/cpaint
* 
* released under the terms of the GPL
* see http://www.fsf.org/licensing/licenses/gpl.txt for details
* 
* @package    CPAINT
* @access     public
* @author     Dominique Stender <dstender@st-webdevelopment.de>
* @copyright  2005 (Dominique Stender); All rights reserved
*/

//---- includes ----------------------------------------------------------------
  /**  
  * @include cpaint ajax library
  */
  require_once('../../cpaint2.inc.php');

//---- functions ---------------------------------------------------------------
  /**
  * will return a list of nicknames from the subscribers file
  *
  * @access  public
  * @return  string
  */
  function retrieveSubscribers() {
    global $cp;

    $a_subscribers  = get_subscriber_list();
    $result_node  =& $cp->add_node('result', 'subscribers');

    foreach ($a_subscribers as $s_line_nickname => $n_line_timestamp) {
      $subscriber_node =& $result_node->add_node('subscriber', $i);
      $subscriber_node->set_data($s_line_nickname);
      $i++;
    }
  }

  /**
  * will return all lines written since $n_time_index as XML
  *
  * @access  public
  * @param   float    $n_time_index   UNIX timestamp with miliseconds
  * @param   string   $s_nickname     nickname of the chatting user
  * @return  string
  */
  function retrieveHistory($n_time_index, $s_nickname) {
    global $cp;

    $n_time    = get_microtime();
    $a_lines  = array();
    $a_subscribers  = array();

    $lines_node    =& $cp->add_node('result', 'lines');
    $subscribers_node  =& $cp->add_node('result', 'subscribers');
    $timeindex_node    =& $cp->add_node('result', 'timeindex');

    // read lines and generate history array
    $a_lines   = get_history($n_time_index);
    $i    = 0;

    foreach ($a_lines as $a_line) {

      if ($a_line['time'] >= $n_time_index && $n_time_index > 0) {
        $line_node  =& $lines_node->add_node('line', $i);
        $nick_node  =& $line_node->add_node('nickname');
        $text_node  =& $line_node->add_node('text');
        $time_node  =& $line_node->add_node('time');

        $nick_node->set_data($a_line['nickname']);
        $text_node->set_data($a_line['line']);
        $time_node->set_data($a_line['time']);
        $i++;
      }
    }

                // add / update subscriber timestamp
    add_subscriber($s_nickname);

    // subscriber list
    $a_subscribers  = get_subscriber_list();
    $i    = 0;

    foreach ($a_subscribers as $s_line_nickname => $n_line_timestamp) {
      $subscriber_node =& $subscribers_node->add_node('subscriber', $i);
      $subscriber_node->set_data($s_line_nickname);
      $i++;
    }

    // misc stuff
    $timeindex_node->set_data($n_time);
  }

  /**
  * adds a line to the history
  * 
  * if the history exceeds 30 lines, the uppermost line is truncated
  *
  * @access   public
  * @param    float   $n_time_index   unix timestamp with microtime
  * @param    string  $s_nickname     nickname of the chatting user
  * @param    string  $s_line         line the user has to say
  * @return   string
  */
  function chat($n_time_index, $s_nickname, $s_line) {
    global $cp;

    add_line($n_time_index, $s_nickname, $s_line);
  
    // return XML
    return retrieveHistory($n_time_index, $s_nickname);

  }

  /**
  * change the subscribers nickname and return the 
  * new nickname / old nickname depending on success
  *
  * @access  public
  * @param  string    $s_old_nick  current nickname
  * @param  string    $s_new_nick  desirec nickname
  * @return  string
  */
  function changeNickname($s_old_nick, $s_new_nick) {
    global $cp;

    $result_node  =& $cp->add_node('result', 'namechange');
    $nick_node  =& $result_node->add_node('nick');

    if (change_subscriber_name($s_old_nick, $s_new_nick) == true) {
      $nick_node->set_data($s_new_nick);

    } else {
      $nick_node->set_data($s_old_nick);
    }
  }



//---- helper functions --------------------------------------------------------
  /**
  * reads the subscriber list and returns an ordered array
  *
  * @access       private
  * @return  array
  */
  function get_subscriber_list() {
    $retval   = array();
    $a_lines  = array();

    // read lines and create XML
    $a_lines        = file('subscribers.txt');

    foreach ($a_lines as $s_line) {
      list($s_line_nickname, $n_line_timestamp) = explode("\t", $s_line);
      $s_line_nickname        = stripslashes($s_line_nickname);
      $s_line_nickname        = clean($s_line_nickname);
      $n_line_timestamp  = clean($n_line_timestamp);

      $retval[$s_line_nickname] = $n_line_timestamp;
    }
    
    return $retval;
  }

  /**
  * writes the subscriber list back to disk
  *
  * @access       private
  * @param  array    $a_subscribers  subscriber list
  * @return  boolean
  */
   function write_subscriber_list($a_subscribers) {
    $retval    = false;
    $fh             = @fopen('subscribers.txt', 'w');
    $n_time    = get_microtime();

    if ($fh !== false) {
      ksort($a_subscribers);
      $retval = true;

      foreach ($a_subscribers as $s_line_nickname => $n_line_timestamp) {
      
        if ($n_line_timestamp > ($n_time - 360)) {
          $retval &= (bool) @fwrite($fh, $s_line_nickname . "\t" . $n_line_timestamp . "\n");
        }
      }
    }

    @fclose($fh);

    return $retval;
  }

  /**
  * changes one nickname in the subscriber list if its not already taken
  *
  * @access  private
  * @param  string    $s_old_nick  current nickname
  * @param  string    $s_new_nick  desired nickname
  * @return  boolean
  */
  function change_subscriber_name($s_old_nick, $s_new_nick) {
    $retval    = false;
    $s_old_nick  = clean($s_old_nick);
    $s_new_nick  = clean($s_new_nick);
    $a_subscribers  = get_subscriber_list();
    $n_time    = get_microtime();

    if (!isset($a_subscribers[$s_new_nick])) {
      unset($a_subscribers[$s_old_nick]);
      $a_subscribers[$s_new_nick]  = $n_time;
      
      $retval        = write_subscriber_list($a_subscribers);
    }

    return $retval;
  }

  /**
  * adds a subscriber to the list
  *
  * @access  private
  * @param  string    $s_nickname  the suscriber name to add
  * @return  boolean
  */
  function add_subscriber($s_nickname) {
    $retval    = false;
    $s_nickname  = clean($s_nickname);
    $n_time    = get_microtime();
    $a_subscribers  = get_subscriber_list();

    if ($s_nickname != '') {
      $a_subscribers[$s_nickname] = $n_time;
      $retval = write_subscriber_list($a_subscribers);
    }

    return $retval;
  }

        /**
        * returns the list of lines spoken after a specific point in time
        *
        * @access       private
  * @param  float    $n_time_index  point in time after which messages are to be retrieved
        * @return  array
        */
   function get_history($n_time_index) {
    $retval    = array();
    $a_lines        = file('history.txt');
    $i    = 0;

    foreach ($a_lines as $s_line) {
      list($n_line_time, $s_line_nickname, $s_line_line) = explode("\t", $s_line);
      $s_line_nickname        = stripslashes($s_line_nickname);
      $s_line_line            = stripslashes($s_line_line);
      $s_line_line            = clean($s_line_line);

      $retval[$i] = array(
        'nickname'  => $s_line_nickname,
        'line'    => $s_line_line,
        'time'    => $n_line_time,
      );
      $i++;
    }

    return $retval;
  }

  /**
  * adds a line to the history array
  * will truncate the history array to a maximum of 30 lines
  *
  * @access  private
  * @param  float    $n_time_index  timestamp
  * @param  string    $s_nickname  subscriber name that spoke
  * @param  string    $s_line    line spoken
  * @return  boolean
  */
  function add_line($n_time_index, $s_nickname, $s_line) {
    $retval       = false;
    $a_lines      = get_history($n_time_index);
    $n_time       = get_microtime();
    $s_nickname   = clean($s_nickname);
    $s_line       = clean($s_line);

    // append new line
    $a_lines[] = array(
      'time'      => $n_time,
      'nickname'  => $s_nickname,
      'line'      => $s_line,
    );

    // truncate first line if array is too large
    while (count($a_lines) > 30) {
      array_shift($a_lines);
    }

    if ($s_nickname != '') {
      // write file back to disk
      $fh = @fopen('history.txt', 'w');
  
      if ($fh !== false) {
        $retval = true;

        foreach ($a_lines as $a_line) {
          $retval &= (bool) @fwrite($fh, $a_line['time'] . "\t" . $a_line['nickname'] . "\t" . $a_line['line'] . "\n");
        }
      }

      @fclose($fh);  
    }
  }

  /**
  * returns the current UNIX timestamp including microseconds
  *
  * @access  public
  * @return  float
  */
  function get_microtime() {
    list($usec, $sec) = explode(" ", microtime());
    return ((float)$usec + (float)$sec);
  }

  /**
  * string clean up
  *
  * @access  private
  * @param  string    $input    input string to clean
  * @return  string
  */
  function clean($input) {
    $retval = $input;
    $retval = str_replace("\t", ' ', $retval);
    $retval = str_replace("\r", '', $retval);
    $retval = str_replace("\n", '', $retval);
    $retval = str_replace('<', '&lt;', $retval);
    $retval = str_replace('>', '&gt; ', $retval);
  
    return $retval;
  }

//---- variables ---------------------------------------------------------------
  $cp = new cpaint();

//---- logic -------------------------------------------------------------------
  $cp->register('chat');
  $cp->register('retrieveHistory');
  $cp->register('retrieveSubscribers');
  $cp->register('changeNickname');
  $cp->start();
  $cp->return_data();

?>
